#include "hash/embedding.h"
#include "hash/common.h"
#include "hash/metric.h"
#include <string.h>

using namespace cspace_learning;

void test_rp();
void test_sparse_rp();
void test_dp_rp();
void test_embedding();
void test_embedding2();
int main()
{
  test_rp();
  std::cout << "-------" << std::endl;
  test_sparse_rp();
  std::cout << "-------" << std::endl;
  test_dp_rp();
  std::cout << "-------" << std::endl;
  test_embedding();
  test_embedding2();
  return 1;
}

void test_rp()
{
  unsigned int N = 100;
  float ub = 10, lb = 0;

  unsigned int dim_in = 50, dim_out = 20;
  DefaultRng rng;
  DistanceConservativeRandomProjector rd(dim_in, dim_out);
  rd.init(rng);
  metric::l2sqr<float> l2_metric1(dim_in);
  metric::l2sqr<float> l2_metric2(dim_out);

  float* data1 = new float[dim_in];
  for(unsigned int d = 0; d < dim_in; ++d)
    data1[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;
  float* data1_r = new float[dim_out];
  memcpy(data1_r, rd(data1), sizeof(float) * dim_out);

  for(unsigned int i = 0; i < dim_in; ++i)
    std::cout << data1[i] << " ";
  std::cout << std::endl;

  for(unsigned int i = 0; i < dim_out; ++i)
    std::cout << data1_r[i] << " ";
  std::cout << std::endl;

  float* data2 = new float[dim_in];
  for(unsigned int i = 0; i < N; ++i)
  {
    for(unsigned int d = 0; d < dim_in; ++d)
      data2[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;

    float dist1 = l2_metric1(data1, data2);
    float dist2 = l2_metric2(data1_r, rd(data2));
    std::cout << dist1 << " " << dist2 << std::endl;
  }

  delete [] data1;
  delete [] data2;
}

void test_sparse_rp()
{
  unsigned int N = 100;
  float ub = 10, lb = 0;

  unsigned int dim_in = 200, dim_out = 100;
  DefaultRng rng;
  DistanceConservativeSparseRandomProjector rd(dim_in, dim_out, 3);
  rd.init(rng);
  metric::l2sqr<float> l2_metric1(dim_in);
  metric::l2sqr<float> l2_metric2(dim_out);

  float* data1 = new float[dim_in];
  for(unsigned int d = 0; d < dim_in; ++d)
    data1[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;
  float* data1_r = new float[dim_out];
  memcpy(data1_r, rd(data1), sizeof(float) * dim_out);

  for(unsigned int i = 0; i < dim_in; ++i)
    std::cout << data1[i] << " ";
  std::cout << std::endl;

  for(unsigned int i = 0; i < dim_out; ++i)
    std::cout << data1_r[i] << " ";
  std::cout << std::endl;

  float* data2 = new float[dim_in];
  for(unsigned int i = 0; i < N; ++i)
  {
    for(unsigned int d = 0; d < dim_in; ++d)
      data2[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;

    float dist1 = l2_metric1(data1, data2);
    float dist2 = l2_metric2(data1_r, rd(data2));
    std::cout << dist1 << " " << dist2 << std::endl;
  }

  delete [] data1;
  delete [] data2;
}

void test_dp_rp()
{
  unsigned int N = 100;
  float ub = 10, lb = 0;

  unsigned int dim = 200;
  unsigned int sampleN = 100;
  DefaultRng rng;
  DotProductConservativeRandomProjector rd(dim);
  rd.init(sampleN, rng);
  dotprod<float> dot(dim);

  float* data1 = new float[dim];
  for(unsigned int d = 0; d < dim; ++d)
    data1[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;


  float* data2 = new float[dim];
  for(unsigned int i = 0; i < N; ++i)
  {
    for(unsigned int d = 0; d < dim; ++d)
      data2[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;

    float dist1 = dot(data1, data2);
    float dist2 = dot(rd(data2), data1);
    std::cout << dist1 << " " << dist2 << std::endl;
  }

  delete [] data1;
  delete [] data2;
}

float distance_sqr(float* v, float* a, float* x, unsigned int dim)
{
  float dist = 0;
  for(unsigned int d = 0; d < dim; ++d)
  {
    dist += (x[d] - a[d]) * (x[d] - a[d]);
  }

  float tmp = 0;
  for(unsigned int d = 0; d < dim; ++d)
  {
    tmp += (x[d] - a[d]) * v[d];
  }

  return dist - tmp * tmp;
}

void test_embedding()
{
  float ub = 10, lb = 0;
  unsigned int dim = 10;
  Embedding embed(dim);
  embed.t() = 0;
  dotprod<float> dotl2((dim + 2) * (dim + 2));

  float* v = new float[dim];
  for(unsigned int d = 0; d < dim; ++d)
    v[d] =  (ub - lb) * (rand() / (float)RAND_MAX) + lb;
  float len = 0;
  for(unsigned int d = 0; d < dim; ++d)
    len += v[d] * v[d];
  len = sqrt(len);
  for(unsigned int d = 0; d < dim; ++d)
    v[d] /= len;


  float* a = new float[dim];
  for(unsigned int d = 0; d < dim; ++d)
    a[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;


  float* line = new float[(dim + 2) * (dim + 2)];
  memcpy(line, embed.embedded_line(v, a), sizeof(float) * (dim + 2) * (dim + 2));

  unsigned int N = 10;
  float* data = new float[dim];
  for(unsigned int i = 0; i < N; ++i)
  {
    for(unsigned int d = 0; d < dim; ++d)
    {
      data[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;
    }

    float dist_sqr_1 = distance_sqr(v, a, data, dim);
    float dist_sqr_2 = dotl2(line, embed.embedded_datapoint(data));
    std::cout << dist_sqr_1 << " " << dist_sqr_2 << std::endl;
  }

  delete [] data;
  delete [] v;
  delete [] a;
  delete [] line;
}

void test_embedding2()
{
  float ub = 10, lb = 0;
  unsigned int dim = 10;
  unsigned int dim_out = (dim + 2) * (dim + 3) / 2;
  ReducedEmbedding embed(dim);
  embed.t() = 0;
  dotprod<float> dotl2(dim_out);

  float* v = new float[dim];
  for(unsigned int d = 0; d < dim; ++d)
    v[d] =  (ub - lb) * (rand() / (float)RAND_MAX) + lb;
  float len = 0;
  for(unsigned int d = 0; d < dim; ++d)
    len += v[d] * v[d];
  len = sqrt(len);
  for(unsigned int d = 0; d < dim; ++d)
    v[d] /= len;


  float* a = new float[dim];
  for(unsigned int d = 0; d < dim; ++d)
    a[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;


  float* line = new float[dim_out];
  memcpy(line, embed.embedded_line(v, a), sizeof(float) * dim_out);

  unsigned int N = 10;
  float* data = new float[dim];
  for(unsigned int i = 0; i < N; ++i)
  {
    for(unsigned int d = 0; d < dim; ++d)
    {
      data[d] = (ub - lb) * (rand() / (float)RAND_MAX) + lb;
    }

    float dist_sqr_1 = distance_sqr(v, a, data, dim);
    float dist_sqr_2 = dotl2(line, embed.embedded_datapoint(data));
    std::cout << dist_sqr_1 << " " << dist_sqr_2 << std::endl;
  }

  delete [] data;
  delete [] v;
  delete [] a;
  delete [] line;
}

